#!/usr/bin/env python3
# -*- coding:utf-8 -*-
## author : cypro666
## date   : 2014.5.23
"""
Wrapper of mongodb driver
"""
import sys
from copy import deepcopy
try:
    import pymongo
    from pymongo import MongoClient, ReadPreference
    #from bson.objectid import ObjectId
    from bson.code import Code
except ImportError as e:
    sys.stderr.write('opmongo.py : Can\'t find pymongo module!\n')

from .configs import MONGO_PORT, MONGO_HOST, MONGO_DEFAULTDB
from .debug import time_meter, print_exception

__all__ = ['OpMongo']


class OpMongo(object):
    """Wrapper of mongodb driver, see configs for more details, not thread-safe"""
    
    def __init__(self, dbname = MONGO_DEFAULTDB):
        super(OpMongo, self).__init__()
        try:
            self.__mc = MongoClient(MONGO_HOST, MONGO_PORT) # see configs.py
            self.__db = self.__mc[dbname]
            self.__coll = None
        except Exception as e:
            print_exception('OpMongo.__init__')
            raise e

    def __del__(self):
        self.__mc.close()
        
    def lists(self):
        assert self.__db
        return deepcopy(self.__db.collection_names())
    
    def connect(self, collname):
        assert isinstance(collname, str)
        self.__coll = self.__db[str(collname)]
    
    def info(self):
        s = 'connected to: %s\nchosen collection: %s\n'
        if self.__coll.count(): 
            s += 'Warning : %s is Not Empty !!!' % self.__coll.full_name
        return s % (str(self.__db), self.__coll.full_name)
    
    def remove(self):
        if self.__coll.count():
            sure = eval(input("Are you sure remove old collection? yes/no\n"))
            if sure.upper() == 'YES' or sure.upper() == 'Y':
                self.__remove__()
                sys.stderr.write('%s removed!\n' % self.__coll.full_name) 
    
    def __remove__(self):
        self.__coll.remove()
    
    
    def execute(self, javascript, *args):
        result = self.__db.command('$eval', 
                                    Code(javascript),
                                    read_preference = ReadPreference.PRIMARY,
                                    args = args)
        return result.get('retval', None)
    
    
    def update(self, spec, doc, upsert = False):
        try:
            self.__coll.update(spec, doc, upsert = upsert)
        except Exception as e:
            print_exception('OpMongo.update')
    
    
    @time_meter('OpMongo.insert')
    def insert(self, docs):
        try:
            objects_id = self.__coll.insert(docs, fsync = False) # True is not necessary!
            sys.stderr.write('coll.count : %d\n' % self.__coll.count())
            return objects_id
        except Exception as e:
            print_exception('OpMongo.insert')
            return None
    
    
    def count(self):
        return self.__coll.count()
    
    
    def create_indexes(self, indexlist):
        try:
            if indexlist:
                index = list(map(self.__coll.create_index, indexlist))
                sys.stderr.write('indexes : %s\n' % str(index))
        except Exception:
            print_exception('OpMongo.create_index')
    
    
    def find_one(self, query):
        try:
            cursor = self.__coll.find_one(query)
            return cursor
        except Exception as e:
            print_exception('OpMongo.find_one')
            return None
    
    
    def explain(self, cursor):
        s = str(cursor.explain()).replace('u\'', '\'')
        s = s.lstrip('{').rstrip('}').replace(',', ',\n    ')
        sys.stderr.write('-------------------- Mongo Explain --------------------\n', end=' ')
        sys.stderr.write('{\n    %s\n }\n' % s)
        sys.stderr.write('-------------------------------------------------------\n')
    
    
    def find(self, query = {}, sortkey = None, reverse = True, explain = True):
        try:
            cursor = self.__coll.find(query)
            if explain:
                self.explain(cursor)
            if sortkey:
                cursor.sort(sortkey, pymongo.DESCENDING if reverse else pymongo.ASCENDING)
            return cursor
        except Exception as e:
            print_exception('OpMongo.find')
            return None



@time_meter(__name__)
def test(collname):
    ''' Simple tester for opmongo '''
    db = OpMongo(collname)
    colls = db.lists()
    db.connect(colls[1])
    db.find({}, explain=True)
    js = """
    function show_collections() {
        collnames = db.getCollectionNames();
        collnames.forEach(function(item, index, arr){ arr[index] = index + " " + item });
        var info = {
            dbname : db.getName(),
            collnames : collnames
        }
        return info
    }
    """
    for each in db.execute(js).items():
        sys.stderr.write(str(each))

    






